using System;
using System.IO;
using System.Reflection;
using System.Collections;
using System.Xml;

namespace NDocBuilder
{
	/// <summary>
	/// The class NDocBuilderConfig is responsible for reading the configuration settings of NDocBuilder.
	/// </summary>
	/// <remarks>
	/// The <see cref="Builder"/> class will call <see cref="NDocBuilderConfig.Load"/> when a new build has been started. This will load the configuration settings file.<br/>
	/// Configuration settings are stored in the file <i>NDocBuilder.Config.Xml</i>.
	/// <seealso cref="Builder.Configuration"/>
	/// </remarks>
	/// 
	public class NDocBuilderConfig
	{
		/// <summary>
		/// The one and only constructor of the NDocBuilderConfig class
		/// </summary>
		public NDocBuilderConfig()
		{
		}

		#region Properties

		private NDocProject m_CurrentProject;
		/// <summary>
		/// The project that pwns this config
		/// </summary>
		public NDocProject CurrentProject
		{
			get{return m_CurrentProject;}
			set{m_CurrentProject=value;}
		}

		private string ConfigFile
		{
			get
			{
#if DEBUG
				return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), @"..\..\NDocBuilder.Config.Xml");
#else
				return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "NDocBuilder.Config.Xml");			 
#endif
			}
		}

		private string[] m_arrResourceDirectories	= {};
		/// <summary>
		/// An string array containing the directories to look for when searching for resources to include.
		/// </summary>
		/// <remarks>
		/// If one of the following directories exists they will be included into the compilation.
		/// This is usefull when one of the projects makes use of images, javascript or any other resources.
		/// If the path is not rooted it is assumed that the path is relative from the project.
		/// </remarks>
		public string[] ResourceDirectories
		{
			get{return m_arrResourceDirectories;}
		}
		
		private NDocBuilderPropertyList m_DocumenterProperties = new NDocBuilderPropertyList();
		/// <summary>
		/// The properties of the documenter
		/// <seealso cref="NDocBuilderPropertyList"/>
		/// <seealso cref="NDocBuilderProperty"/>
		/// </summary>
		public NDocBuilderPropertyList DocumenterProperties
		{
			get{return m_DocumenterProperties;}
		}

		#endregion

		private void LoadResourceDirectories(XmlNodeList xmlList)
		{
			m_arrResourceDirectories = new string[xmlList.Count];

			for(int i = 0; i < xmlList.Count; i++)
				m_arrResourceDirectories[i] = xmlList[i].Attributes["value"].Value;
		}

		private void LoadDocumenterProperties(XmlNodeList xmlList, bool bDirectories)
		{
			for(int i = 0; i < xmlList.Count; i++)
			{
				string strName = xmlList[i].Attributes["name"].Value;
				string strValue = xmlList[i].Attributes["value"].Value;

				DocumenterProperties.Add(new NDocBuilderProperty(strName, strValue, bDirectories));
			}
		}

		/// <summary>
		/// Loads and parses the configuration file
		/// </summary>
		public void Load()
		{
			Builder b = Builder.Current;

			b.OutputPane.OutputString("\nLoading configuration ...\n");

			try
			{
				XmlDocument xmlDoc		= new XmlDocument();
				XmlNodeList xmlNodes	= null;

				xmlDoc.Load(ConfigFile);
				
				xmlNodes	= xmlDoc.SelectNodes("/configuration/NDocBuilderSettings/ResourceDirectories/ResourceDirectory");
				LoadResourceDirectories(xmlNodes);
				
				xmlNodes	= xmlDoc.SelectNodes("/configuration/NDocBuilderSettings/documenterSettings/properties/property");
				LoadDocumenterProperties(xmlNodes, false);

				/*xmlNodes	= xmlDoc.SelectNodes("/configuration/NDocBuilderSettings/documenterSettings/directories/directory");
				LoadDocumenterProperties(xmlNodes, true);*/
			}
			catch(Exception ex)
			{
				throw(new Exception("Something went wrong while reading the configuration file: \"" + ConfigFile + "\"\n" + ex.ToString()));	 
			}

			b.OutputPane.OutputString("Configuration loaded.\n");
		}

		/// <summary>
		/// This function is used to replace tokens in property values found in the configuration file.
		/// </summary>
		/// <param name="strValue">The value to search for tokens</param>
		/// <returns>The modified value</returns>
		public string ReplaceTokens(string strValue)
		{
			strValue = strValue.Replace("{ProjectFolder}", CurrentProject.ActiveProjectAssembly.ProjectPath);
			strValue = strValue.Replace("{ProjectNamespace}", CurrentProject.ActiveProjectAssembly.Namespace);
			strValue = strValue.Replace("{Day}", DateTime.Now.Day.ToString());
			strValue = strValue.Replace("{Month}", DateTime.Now.Month.ToString());
			strValue = strValue.Replace("{Year}", DateTime.Now.Year.ToString());

			return strValue;
		}
	}

	/// <summary>
	/// The class NDocBuilderPropertyList is a collection holder for the <see cref="NDocBuilderProperty"/> class.
	/// <seealso cref="NDocBuilderConfig"/>
	/// </summary>
	public class NDocBuilderPropertyList : CollectionBase
	{
		/// <summary>
		/// Get a NDocBuilderProperty by the given index
		/// </summary>
		public NDocBuilderProperty this[int nIdx]
		{
			get{return (NDocBuilderProperty)List[nIdx];}
		}

		/// <summary>
		/// Get a NDocBuilderProperty by name. Will return null if the name if not found.
		/// </summary>
		public NDocBuilderProperty this[string strName]
		{
			get
			{
				for(int i = 0; i < Count; i++)
				{
					NDocBuilderProperty ret = this[i];

					if(strName == ret.Name)
						return ret;
				}

				return null;
			}
		}

		/// <summary>
		/// Add a property to the list
		/// </summary>
		/// <param name="property">The property to add</param>
		/// <returns>The index of the property</returns>
		public int Add(NDocBuilderProperty property)
		{
			return List.Add(property);
		}
	}

	/// <summary>
	/// The NDocBuilderProperty class is a holder for configuration properties
	/// <seealso cref="NDocBuilderConfig"/>
	/// </summary>
	public class NDocBuilderProperty
	{
		/// <summary>
		/// The constructor of the NDocBuilderProperty class
		/// <seealso cref="NDocBuilderConfig"/>
		/// </summary>
		/// <param name="strName">The name of the property</param>
		/// <param name="strValue">The value of this property</param>
		public NDocBuilderProperty(string strName, string strValue) : this(strName, strValue, false)
		{}
	
		/// <summary>
		/// The constructor of the NDocBuilderProperty class
		/// <seealso cref="NDocBuilderConfig"/>
		/// </summary>
		/// <param name="strName">The name of the property</param>
		/// <param name="strValue">The value of this property</param>
		/// <param name="bIsDirectory">Indicates the value contains a directory</param>
		public NDocBuilderProperty(string strName, string strValue, bool bIsDirectory)
		{
			m_strName = strName;
			m_strValue = strValue;
			m_bIsDirectory = bIsDirectory;
		}

		private string m_strName;
		/// <summary>
		/// The name of this property
		/// </summary>
		public string Name
		{
			get{return m_strName;}
		}

		private string m_strValue;
		/// <summary>
		/// The value of this property
		/// </summary>
		public string Value
		{
			get
			{
				return Builder.Current.Configuration.ReplaceTokens(m_strValue);
			}
		}

		private bool m_bIsDirectory;
		/// <summary>
		/// Indicates if the value contains a directory
		/// </summary>
		public bool IsDirectory
		{
			get{return m_bIsDirectory;}
		}
	}
}
